﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace ExpressionMapper
{
    public sealed class ReferenceDictionary
    {
        private int _referenceId = 0;
        private readonly Dictionary<ReferenceKey, object> _references = new Dictionary<ReferenceKey, object>();

        private sealed class ReferenceKey
        {
            public ReferenceKey(int referenceId, object key)
            {
                Id = referenceId;
                Key = key;
            }

            private int Id { get; set; }
            internal object Key { get; private set; }

            public override bool Equals(object obj)
            {
                if (obj == null)
                    return false;
                ReferenceKey reference = obj as ReferenceKey;
                if (reference == null)
                    return false;

                return Id == reference.Id;
            }

            public override int GetHashCode()
            {
                return Id;
            }
        }

        public void Add(object key, object value)
        {
            lock (_references)
            {
                if (getReferenceKey(key) != null)
                {
                    throw new ArgumentException("An item with the same key has already been added.", "key");
                }
                _references.Add(new ReferenceKey(_referenceId, key), value);
                _referenceId += 1;
            }
        }

        public bool TryGetValue(object key, out object value)
        {
            lock (_references)
            {
                value = null;
                if (key == null)
                    return false;

                Type keyType = key.GetType();
                if (!keyType.IsClass)
                    throw new ArgumentException("Argument should be a reference type", "key");

                ReferenceKey dictionaryKey = getReferenceKey(key);
                if (dictionaryKey != null)
                {
                    value = _references[dictionaryKey];
                    return true;
                }
                return false;
            }
        }

        private ReferenceKey getReferenceKey(object key)
        {
            lock (_references)
            {
                return _references.Keys.FirstOrDefault(dictionaryKey => ReferenceEquals(dictionaryKey.Key, key));
            }
        }
    }

   
}
